package com.shdanyan.config.security;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.shdanyan.config.filter.StatelessAuthenticationFilter;
import com.shdanyan.config.filter.StatelessLoginFilter;
import com.shdanyan.security.CustomUserDetailsService;
import com.shdanyan.security.TokenAuthenticationService;
import com.shdanyan.service.PermissionInfoService;

@Order(1)
@Configuration
@EnableConfigurationProperties(SecuritySettings.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    // @Autowired
    // private CrosFilter crosFilter;

    @Autowired
    private SecuritySettings settings;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private TokenAuthenticationService tokenAuthenticationService;

    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Autowired
    PermissionInfoService permissionInfoService;

    public WebSecurityConfig() {
        super(true);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable();

        // h2 database console
        // http.headers().frameOptions().disable();
        // http.addFilterBefore(crosFilter, ChannelProcessingFilter.class);

        http.exceptionHandling().and().anonymous().and().servletApi().and().headers()
                .cacheControl();

        http.authorizeRequests()
                .antMatchers(settings.getPermitall().split(",")).permitAll();
        // .antMatchers(HttpMethod.GET, "/users/**").hasRole("USER");

        /*
         * .antMatchers(HttpMethod.POST, "/api/posts/**").hasRole("USER")
         * .antMatchers(HttpMethod.GET, "/users").hasRole("USER")
         * .antMatchers(HttpMethod.GET, "/console/**").permitAll()
         */

        // .and().csrf().requireCsrfProtectionMatcher(csrfSecurityRequestMatcher());

        http.addFilterBefore(
                new StatelessLoginFilter("/api/login", tokenAuthenticationService,
                        customUserDetailsService, authenticationManager()),
                UsernamePasswordAuthenticationFilter.class);

        http.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService),
                UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customUserDetailsService)
                .passwordEncoder(passwordEncoder());
    }

    @Override
    protected UserDetailsService userDetailsService() {
        return customUserDetailsService;
    }

    @Bean
    public CustomFilterSecurityInterceptor customFilter() throws Exception {
        CustomFilterSecurityInterceptor customFilter = new CustomFilterSecurityInterceptor();
        customFilter.setSecurityMetadataSource(securityMetadataSource());
        customFilter.setAccessDecisionManager(accessDecisionManager());
        customFilter.setAuthenticationManager(authenticationManager);
        return customFilter;
    }

    @Bean
    public CustomSecurityMetadataSource securityMetadataSource() {
        return new CustomSecurityMetadataSource(settings.getUrlroles(), permissionInfoService);
    }

    @Bean
    public CustomAccessDecisionManager accessDecisionManager() {
        return new CustomAccessDecisionManager();
    }

    private CsrfSecurityRequestMatcher csrfSecurityRequestMatcher() {
        CsrfSecurityRequestMatcher csrfSecurityRequestMatcher = new CsrfSecurityRequestMatcher();
        List<String> list = new ArrayList<String>();
        list.add("/rest/");
        csrfSecurityRequestMatcher.setExecludeUrls(list);
        return csrfSecurityRequestMatcher;
    }
}